#pragma once

#include <vex/policy/protect_policy.h>
#include <vex/iterable/range_base.h>
#include <vex/iterable/adapter/adapter_access.h>
#include <vex/iterable/adapter/adapter_default.h>
#include <type_traits>

namespace vex {
    namespace iterable {
        namespace any {
            namespace detail {
                template<class TRange, class TRangeTraits, class TCategory = trivial_tag>
                struct implements_range_concept : public range_base<
                    typename TRangeTraits::category,
                    typename TRangeTraits::reference,
                    range_marker
                >
                {
                    bool move_front()
                    {
                        return static_cast<TRange*>(this)->get()->move_front();
                    }
                };

                template<class TRange, class TRangeTraits>
                struct implements_range_concept<TRange, TRangeTraits, output_tag>
                    : public implements_range_concept<TRange, TRangeTraits, trivial_tag>
                {
                    template<class TValue>
                    bool put(TValue&& p_value)
                    {
                        return static_cast<TRange*>(this)->get()->put(std::forward<TValue>(p_value));
                    }
                };

                template<class TRange, class TRangeTraits>
                struct implements_range_concept<TRange, TRangeTraits, one_pass_tag>
                    : public implements_range_concept<TRange, TRangeTraits, trivial_tag>
                {
                    typename TRangeTraits::reference front() const
                    {
                        return static_cast<TRange const*>(this)->get()->front();
                    }
                };

                template<class TRange, class TRangeTraits>
                struct implements_range_concept<TRange, TRangeTraits, forward_tag>
                    : public implements_range_concept<TRange, TRangeTraits, one_pass_tag>
                {
                    TRange save() const
                    {
                        return TRange(static_cast<TRange const*>(this)->get()->save(), false);
                    }
                };

                template<class TRange, class TRangeTraits>
                struct implements_range_concept<TRange, TRangeTraits, bidirectional_tag>
                    : public implements_range_concept<TRange, TRangeTraits, forward_tag>
                {
                    bool move_back()
                    {
                        return static_cast<TRange*>(this)->get()->move_back();
                    }

                    typename TRangeTraits::reference back() const
                    {
                        return static_cast<TRange const*>(this)->get()->back();
                    }
                };

                template<class TRange, class TRangeTraits>
                struct implements_range_concept<TRange, TRangeTraits, random_access_tag>
                    : public implements_range_concept<TRange, TRangeTraits, bidirectional_tag>
                {
                    bool move_front_of(std::size_t p_diff)
                    {
                        return static_cast<TRange*>(this)->get()->move_front_of(p_diff);
                    }

                    bool move_back_of(std::size_t p_diff)
                    {
                        return static_cast<TRange*>(this)->get()->move_back_of(p_diff);
                    }

                    std::size_t size() const
                    {
                        return static_cast<TRange const*>(this)->get()->size();
                    }

                    typename TRangeTraits::reference at(std::size_t p_index) const
                    {
                        return static_cast<TRange const*>(this)->get()->at(p_index);
                    }

                    VEX_PROTECT_POLICY(implements_range_concept);
                };
            }

            template<class TClass, class TClassTraits>
            struct implements_range_concept_policy : public detail::implements_range_concept<
                TClass,
                TClassTraits,
                typename TClassTraits::category
            >
            {
                VEX_PROTECT_POLICY(implements_range_concept_policy);
            };
        }
    }
}